# Copyright (C) Kumo inc. and its affiliates.
# Author: Jeff.li lijippy@163.com
# All rights reserved.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https:#www.gnu.org/licenses/>.
#

# MODULE:   ThirdpartyToolchain
# #
# Provides the ability to resolve third party dependencies. If the dependency is
# already available in the system it will be used.
#
# The dependency_name argument is required. The dependencyName value will be
# used to search for the installed dependencies Config file and thus this name
# should match find_package() standards.
#
# EXAMPLE USAGE: # Download and setup or use already installed dependency.
#
# ==============================================================================

include(FetchContent)
include(ExternalProject)
include(ProcessorCount)
include(CheckCXXCompilerFlag)
list(APPEND CMAKE_MODULE_PATH
     ${CMAKE_CURRENT_LIST_DIR}/resolve_dependency_modules)

# Enable SSL certificate verification for file downloads
set(CMAKE_TLS_VERIFY true)

macro(pollux_build_dependency dependency_name)
  string(TOLOWER ${dependency_name} dependency_name_lower)
  include(${dependency_name_lower})
endmacro()

# * Macro to resolve third-party dependencies.
#
# find the dependency via the usage of find_package or use the custom
# pollux_build_dependency(dependency_name) macro to download and build the third
# party dependency.
#
# pollux_resolve_dependency(dependency_name [...] )
#
# [...]: the macro will pass all arguments after DEPENDENCY_NAME on to
# find_package. ${dependency_name}_SOURCE is expected to be set to either AUTO,
# SYSTEM or BUNDLED. If ${dependency_name}_SOURCE is AUTO it will try to find
# the corresponding package via find_package and if not found it will call the
# build_dependency macro to download and build the third party dependency. If
# ${dependency_name}_SOURCE is SYSTEM it will force to find via find_package. If
# ${dependency_name}_SOURCE is BUNDLED it will force to build from source.
#
# In the case of setting ${dependency_name}_SOURCE to SYSTEM if the dependency
# is not found the build will fail and will not fall back to download and build
# from source.
macro(pollux_resolve_dependency dependency_name)
  # INDENT is always visible, CONTEXT has to be turned on.
  list(APPEND CMAKE_MESSAGE_INDENT "[${dependency_name}] ")

  set(find_package_args ${dependency_name} ${ARGN})
  list(REMOVE_ITEM find_package_args REQUIRED QUIET)
  if(${dependency_name}_SOURCE STREQUAL "AUTO")
    find_package(${find_package_args})
    if(${${dependency_name}_FOUND})
      set(${dependency_name}_SOURCE "SYSTEM")
    else()
      set(${dependency_name}_SOURCE "BUNDLED")
      pollux_build_dependency(${dependency_name})
    endif()
    message(STATUS "Using ${${dependency_name}_SOURCE} ${dependency_name}")
  elseif(${dependency_name}_SOURCE STREQUAL "SYSTEM")
    find_package(${find_package_args} REQUIRED)
  elseif(${dependency_name}_SOURCE STREQUAL "BUNDLED")
    pollux_build_dependency(${dependency_name})
  else()
    message(
      FATAL_ERROR
        "Invalid source for ${dependency_name}: ${${dependency_name}_SOURCE}")
  endif()

  list(POP_BACK CMAKE_MESSAGE_INDENT)
endmacro()

# By using a macro we don't need to propagate the value into the parent scope.
macro(pollux_set_source dependency_name)
  pollux_set_with_default(${dependency_name}_SOURCE ${dependency_name}_SOURCE
                         ${POLLUX_DEPENDENCY_SOURCE})
  message(
    STATUS "Setting ${dependency_name} source to ${${dependency_name}_SOURCE}")
endmacro()

# Set var_name to the value of $ENV{envvar_name} if ENV is defined. If neither
# ENV or var_name is defined then set var_name to ${DEFAULT}. If called from
# within a nested scope the variable will not propagate into outer scopes
# automatically! Use PARENT_SCOPE.
function(pollux_set_with_default var_name envvar_name default)
  if(DEFINED ENV{${envvar_name}})
    set(${var_name}
        $ENV{${envvar_name}}
        PARENT_SCOPE)
  elseif(NOT DEFINED ${var_name})
    set(${var_name}
        ${default}
        PARENT_SCOPE)
  endif()
endfunction()

# Set custom source url with a optional sha256 checksum.
macro(pollux_resolve_dependency_url dependency_name)
  # Prepend prefix for default checksum.
  string(PREPEND POLLUX_${dependency_name}_BUILD_SHA256_CHECKSUM "SHA256=")

  pollux_set_with_default(
    POLLUX_${dependency_name}_SOURCE_URL POLLUX_${dependency_name}_URL
    ${POLLUX_${dependency_name}_SOURCE_URL})
  message(VERBOSE "Set POLLUX_${dependency_name}_SOURCE_URL to "
          "${POLLUX_${dependency_name}_SOURCE_URL}")
  if(DEFINED ENV{POLLUX_${dependency_name}_URL})
    pollux_set_with_default(POLLUX_${dependency_name}_BUILD_SHA256_CHECKSUM
                           POLLUX_${dependency_name}_SHA256 "")
    if(DEFINED ENV{POLLUX_${dependency_name}_SHA256})
      string(PREPEND POLLUX_${dependency_name}_BUILD_SHA256_CHECKSUM "SHA256=")
    endif()
  endif()
endmacro()
